package com.slf.quant.facade.sdk.huobi.util;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ApiSignature
{
    final Logger                   log       = LoggerFactory.getLogger(getClass());
    
    static final DateTimeFormatter DT_FORMAT = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss");
    
    static final ZoneId            ZONE_GMT  = ZoneId.of("Z");
    
    /**
     * 创建一个有效的签名。该方法为客户端调用，将在传入的params中添加AccessKeyId、Timestamp、SignatureVersion、SignatureMethod、Signature参数。
     *
     * @param appKey
     *            AppKeyId.
     * @param appSecretKey
     *            AppKeySecret.
     * @param method
     *            请求方法，"GET"或"POST"
     * @param host
     *            请求域名，例如"be.huobi.com"
     * @param uri
     *            请求路径，注意不含?以及后的参数，例如"/v1/api/info"
     * @param params
     *            原始请求参数，以Key-Value存储，注意Value不要编码
     */
    public void createSignature(String appKey, String appSecretKey, String method, String uri, Map<String, String> params)
    {
        StringBuilder sb = new StringBuilder(1024);
        int index = 0;
        if (uri.indexOf(".pro") > -1)
        {
            index = uri.indexOf(".pro");
        }
        if (uri.indexOf(".io") > -1)
        {
            index = uri.indexOf(".io");
        }
        if (uri.indexOf(".com") > -1)
        {
            index = uri.indexOf(".com");
        }
        int index2 = uri.indexOf("//");
        String host = uri.substring(index2 + 2, index + 4);
        String constant = uri.substring(index + 4, uri.length());
        sb.append(method.toUpperCase()).append('\n') // GET
                .append(host.toLowerCase()).append('\n') // Host
                .append(constant).append('\n'); // /path
        params.remove("Signature");
        params.put("AccessKeyId", appKey);
        params.put("SignatureVersion", "2");
        params.put("SignatureMethod", "HmacSHA256");
        params.put("Timestamp", gmtNow());
        // build signature:
        SortedMap<String, String> map = new TreeMap<>(params);
        for (Map.Entry<String, String> entry : map.entrySet())
        {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key).append('=').append(urlEncode(value)).append('&');
        }
        // remove last '&':
        sb.deleteCharAt(sb.length() - 1);
        // sign:
        Mac hmacSha256 = null;
        try
        {
            hmacSha256 = Mac.getInstance("HmacSHA256");
            SecretKeySpec secKey = new SecretKeySpec(appSecretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            hmacSha256.init(secKey);
        }
        catch (NoSuchAlgorithmException e)
        {
            throw new RuntimeException("No such algorithm: " + e.getMessage());
        }
        catch (InvalidKeyException e)
        {
            throw new RuntimeException("Invalid key: " + e.getMessage());
        }
        String payload = sb.toString();
        byte[] hash = hmacSha256.doFinal(payload.getBytes(StandardCharsets.UTF_8));
        String actualSign = Base64.getEncoder().encodeToString(hash);
        params.put("Signature", actualSign);
        if (log.isDebugEnabled())
        {
            log.debug("Dump parameters:");
            for (Map.Entry<String, String> entry : params.entrySet())
            {
                log.debug("  key: " + entry.getKey() + ", value: " + entry.getValue());
            }
        }
    }
    
    /**
     * 使用标准URL Encode编码。注意和JDK默认的不同，空格被编码为%20而不是+。
     *
     * @param s
     *            String字符串
     * @return URL编码后的字符串
     */
    public static String urlEncode(String s)
    {
        try
        {
            return URLEncoder.encode(s, "UTF-8").replaceAll("\\+", "%20");
        }
        catch (UnsupportedEncodingException e)
        {
            throw new IllegalArgumentException("UTF-8 encoding not supported!");
        }
    }
    
    /**
     * Return epoch seconds
     */
    long epochNow()
    {
        return Instant.now().getEpochSecond();
    }
    
    String gmtNow()
    {
        return Instant.ofEpochSecond(epochNow()).atZone(ZONE_GMT).format(DT_FORMAT);
    }
}
